<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <script>
    const range = (x) => [...Array(x)].map((_, i) => i)
    const pick = (topick, balls, balls_sum) => {
      if (topick === 0) return [balls.map(_ => 0)]
      if (balls_sum < topick) return []
      if (balls_sum === topick) return [balls]
      const [now, ...rest] = balls
      return range(Math.min(now, topick) + 1).map(i => pick(topick - i, rest, balls_sum - now).map(a => [i, ...a])).reduce((r, a) => r.concat(a), [])
    }
    const array = (m) => m > 1 ? range(m - 1).map(x => x + 2).reduce((x, y) => x * y, 1) : 1
    const getProbability = (balls) => {
      const total = balls.reduce((x, y) => x + y)
      const topick = total / 2
      const allPicksLeft = pick(topick, balls, total)
      const sameCaseNums = (a) => a.map(array).reduce((x, y) => x * y, 1)
      const sameColorPicks = allPicksLeft.map(x => ({
        left: x,
        right: balls.map((a, i) => a - x[i])
      })).filter(x => x.left.filter(c => c > 0).length === x.right.filter(c => c > 0).length)
      const totalNums = array(total) / sameCaseNums(balls)
      const squareHalfNums = array(topick) ** 2
      const resultNums = sameColorPicks.map(x => squareHalfNums / sameCaseNums(x.left) / sameCaseNums(x.right)).reduce((x, y) => x + y, 0)
      return resultNums / totalNums
    };
    console.log(getProbability(balls = [2, 1, 1]))
  </script>
</body>

</html>